#include "node.h"
#include <iostream>

Node::Node()
{
	tl_x = 0;
	tl_y = 0;
	br_x = 0;
	br_y = 0;
	nodeLevel = 0;
	
	colorAverage = -1;
	colorAverageBlue = -1;
	colorAverageGreen = -1;
	colorAverageRed = -1;
	
	colored = false;
	nodeAncestor = NULL;
}

Node::Node(int x1, int y1, int x2, int y2, int level, Node* ancestor)
{
	tl_x = x1;
	tl_y = y1;
	br_x = x2;
	br_y = y2;
	nodeLevel = level;
	
	colorAverage = -1;
	colorAverageBlue = -1;
	colorAverageGreen = -1;
	colorAverageRed = -1;
	
	colored = false;
	nodeAncestor = ancestor;
}

Node::~Node()
{
	std::vector<Node*>::iterator it;
	for (int i = 0; i < 4; i++)
	{
		for (it = adjacentMatrix[i].begin(); it != adjacentMatrix[i].end(); )
		{
			it = adjacentMatrix[i].erase(it);
		}
	}
}

void Node::RemoveAdjacentMatrix()
{
	std::vector<Node*>::iterator it;
	for (int i = 0; i < 4; i++)
	{
		for (it = adjacentMatrix[i].begin(); it != adjacentMatrix[i].end(); )
		{
			it = adjacentMatrix[i].erase(it);
		}
	}
}

bool Node::IsHomogeneous(IplImage *img, int threshold, int minSize)
{
	if (minSize < 1)
		minSize = 1;
	
	if ((br_x - tl_x) < minSize || (br_y - tl_y) < minSize)
	{
		this->CalculateColorAverage(img);
		return true;
	}
	
	CvScalar pixel = cvGet2D(img, tl_y, tl_x);
	
	int blueMax = pixel.val[0] + threshold;
	int blueMin = pixel.val[0] - threshold;
	if (blueMax > 255)
		blueMax = 255;
	if (blueMin < 0)
		blueMin = 0;
	
	int greenMax = pixel.val[1] + threshold;
	int greenMin = pixel.val[1] - threshold;
	if (greenMax > 255)
		greenMax = 255;
	if (greenMin < 0)
		greenMin = 0;
	
	int redMax = pixel.val[2] + threshold;
	int redMin = pixel.val[2] - threshold;
	if (redMax > 255)
		redMax = 255;
	if (redMin < 0)
		redMin = 0;
		
	for(int i = tl_x; i <= br_x; i++)
	{
		for(int j = tl_y; j <= br_y; j++)
		{
			if (i != tl_x && j != tl_y)
			{
				pixel = cvGet2D(img, j, i);
				
				if (blueMax < pixel.val[0] || blueMin > pixel.val[0] ||
					greenMax < pixel.val[1] || greenMin > pixel.val[1] ||
					redMax < pixel.val[2] || redMin > pixel.val[2])
					return false;
			}
		}
	}
	
	this->CalculateColorAverage(img);
	return true;
}

bool Node::IsHomogeneousDeviation(IplImage* img, int threshold, int minSize)
{
	if (minSize < 1)
		minSize = 1;
	
	if ((br_x - tl_x) < minSize || (br_y - tl_y) < minSize)
	{
		this->CalculateColorAverage(img);
		return true;
	}
	
	int nbPixels = CalculateNbPixels();
	
	int minCol = 255;
	int maxCol = 0;
	
	int colAvTmp = 0;
	int colAvBlueTmp = 0;
	int colAvGreenTmp = 0;
	int colAvRedTmp = 0;
	
	for (int i = tl_x; i <= br_x; i++)
	{
		for (int j = tl_y; j <= br_y; j++)
		{
			colAvBlueTmp += cvGet2D(img, j, i).val[0];
			colAvGreenTmp += cvGet2D(img, j, i).val[1];
			colAvRedTmp += cvGet2D(img, j, i).val[2];
			
			colAvTmp = (cvGet2D(img, j, i).val[0] + cvGet2D(img, j, i).val[1] + cvGet2D(img, j, i).val[2]) / 3;
			
			if (colAvTmp < minCol)
				minCol = colAvTmp;
			if (colAvTmp > maxCol)
				maxCol = colAvTmp;
		}
	}
	
	colAvBlueTmp /= nbPixels;
	colAvGreenTmp /= nbPixels;
	colAvRedTmp /= nbPixels;
	colAvTmp = (colAvBlueTmp + colAvGreenTmp + colAvRedTmp) / 3;
	
	int colAvTmpMax = colAvTmp + threshold;
	int colAvTmpMin = colAvTmp - threshold;
	if (colAvTmpMax < maxCol || colAvTmpMin > minCol)
		return false;
	
	colorAverageBlue = colAvBlueTmp;
	colorAverageGreen = colAvGreenTmp;
	colorAverageRed = colAvRedTmp;
	colorAverage = colAvTmp;
	
	return true;
}

int Node::CalculateNbPixels()
{
	int nbPixels = 1;
	
	if (tl_x == br_x || tl_y == br_y)
	{
		if (tl_x != br_x && tl_y == br_y)
			nbPixels = (br_x - tl_x) + 1;
		else if (tl_y != br_y && tl_x == br_x)
			nbPixels = (br_y - tl_y) + 1;
		else
			nbPixels = 1;
	}
	else
		nbPixels = (br_x - tl_x + 1) * (br_y - tl_y + 1);
	
	return nbPixels;
}

void Node::CalculateColorAverage(IplImage* img)
{
	int nbPixels = CalculateNbPixels();
	colorAverage = 0;

	colorAverageBlue = 0;
	colorAverageGreen = 0;
	colorAverageRed = 0;
	
	for(int i = tl_x; i <= br_x; i++)
	{
		for(int j = tl_y; j <= br_y; j++)
		{
			colorAverageBlue += cvGet2D(img, j, i).val[0];
			colorAverageGreen += cvGet2D(img, j, i).val[1];
			colorAverageRed += cvGet2D(img, j, i).val[2];
		}
	}
	
	colorAverageBlue /= nbPixels;
	colorAverageGreen /= nbPixels;
	colorAverageRed /= nbPixels;
	colorAverage = (colorAverageBlue + colorAverageGreen + colorAverageRed) / 3;
}

void Node::ColorNode(IplImage* img, CvScalar pixel, int averageMax, int averageMin)
{
	for (int i = tl_x; i <= br_x; i++)
		for (int j = tl_y; j <= br_y; j++)
			cvSet2D(img, j, i, pixel);
		
	colored = true;
	
	for (int k = 0 ; k < 4 ; k++)
	{
		if (!adjacentMatrix[k].empty())
		{
			for (int l = 0 ; l < adjacentMatrix[k].size(); l++)
			{
				if (	!adjacentMatrix[k].at(l)->GetColored() && 
					adjacentMatrix[k].at(l)->GetColorAverage() <= averageMax && 
					adjacentMatrix[k].at(l)->GetColorAverage() >= averageMin)
				{
					adjacentMatrix[k].at(l)->ColorNode(img, pixel, averageMax, averageMin);
				}
			}
		}
	}
}

void Node::InheritAdjacentMatrixTop(std::vector<Node*> &top, bool side)
{	
	// Note : Comme on découpe en largeur on le noeud père ne peut pas avoir plus de 2 voisins.
	// Donc un noeud fils ne peut hérité du père qu'un seul voisin.
	
	if (top.size() == 1)
	{
		adjacentMatrix[0].push_back(top.front());
		top.front()->AddNeighbourBottom(this);
	}
	else if (top.size() == 2)
	{
		if (side)
		{
			adjacentMatrix[0].push_back(top.front());
			top.front()->AddNeighbourBottom(this);
		}
		else
		{
			adjacentMatrix[0].push_back(top.back());
			top.back()->AddNeighbourBottom(this);
		}
	}
}

void Node::InheritAdjacentMatrixRight(std::vector<Node*> &right, bool side)
{
	if (right.size() == 1)
	{
		adjacentMatrix[1].push_back(right.front());
		right.front()->AddNeighbourLeft(this);
	}
	else if (right.size() == 2)
	{
		if (side)
		{
			adjacentMatrix[1].push_back(right.front());
			right.front()->AddNeighbourLeft(this);
		}
		else
		{
			adjacentMatrix[1].push_back(right.back());
			right.back()->AddNeighbourLeft(this);
		}
	}
}

void Node::InheritAdjacentMatrixBottom(std::vector<Node*> &bottom, bool side)
{
	if (!bottom.empty())
	{
		adjacentMatrix[2].push_back(bottom.front());
		bottom.front()->AddNeighbourTop(this);
	}
}

void Node::InheritAdjacentMatrixLeft(std::vector<Node*> &left, bool side)
{
	if (left.size() == 1)
	{
		adjacentMatrix[3].push_back(left.front());
		left.front()->AddNeighbourRight(this);
	}
	else if (left.size() == 2)
	{
		if (side)
		{
			adjacentMatrix[3].push_back(left.front());
			left.front()->AddNeighbourRight(this);
		}
		else
		{
			adjacentMatrix[3].push_back(left.back());
			left.back()->AddNeighbourRight(this);
		}
	}
}

void Node::AddNeighbourTop(Node *neighbour) 
{ 
	std::vector<Node*>::iterator it = adjacentMatrix[0].begin();
	while (it != adjacentMatrix[0].end() && *it != neighbour->nodeAncestor)
		++it;
	
	if (it != adjacentMatrix[0].end())	
		adjacentMatrix[0].erase(it);
	
	adjacentMatrix[0].push_back(neighbour);
}

void Node::AddNeighbourRight(Node *neighbour) 
{ 
	std::vector<Node*>::iterator it = adjacentMatrix[1].begin();
	while (it != adjacentMatrix[1].end() && *it != neighbour->nodeAncestor)
		++it;
	
	if (it != adjacentMatrix[1].end())
		adjacentMatrix[1].erase(it);
	
	adjacentMatrix[1].push_back(neighbour); 
}

void Node::AddNeighbourBottom(Node *neighbour) 
{ 
	std::vector<Node*>::iterator it = adjacentMatrix[2].begin();
	while (it != adjacentMatrix[2].end() && *it != neighbour->nodeAncestor)
		++it;
	
	if (it != adjacentMatrix[2].end())	
		adjacentMatrix[2].erase(it);
	
	adjacentMatrix[2].push_back(neighbour); 
}

void Node::AddNeighbourLeft(Node *neighbour) 
{ 
	std::vector<Node*>::iterator it = adjacentMatrix[3].begin();
	while (it != adjacentMatrix[3].end() && *it != neighbour->nodeAncestor)
		++it;
	
	if (it != adjacentMatrix[3].end())
		adjacentMatrix[3].erase(it);
	
	adjacentMatrix[3].push_back(neighbour); 
}




